;(function(global,factory){
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'))
      : typeof define === 'function' && define.amd ? define(['jquery'],factory)
      : global.layui && layui.define ? layui.define(['jquery'],function (exports) {
            let $ = layui.jquery
            exports('tag',factory($));
          })
          : global.tag = factory($);
})(this,function($){
  let style = document.createElement('style')
  , styleText = [
      '.float-right{float: right;}'
      ,'.tags-container{border:1px solid #E4E4E4;background:#fff;padding:8px;white-space: normal;text-align:left}'
      ,'.tag-tool>*{font-size: 12px;height: 30px;line-height: 30px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}'
      ,'.tag-item{transition: all .2s;-webkit-transition: all .2s;position:relative;cursor: pointer;margin: 7.5px;}'
      ,'.tag-item .tag-close{opacity:0;filter:opacity(0);border-radius: 50%;position: absolute;right: -7px;top: -7px;height: 15px;line-height: 15px;width: 15px;font-size: 12px !important;transition: all .2s;-webkit-transition: all .2s;}'
      ,'.tag-item:hover .tag-close{background-color: #FF5722;color: #fff;opacity:1;filter:opacity(100)}'
      ,'.new-tag-input{width: 120px;height: 30px;vertical-align: top;}'
      ,'layui-btn-container .new-tag-input.layui-btn{margin: 7.5px;}'
      ,'.new-tag-input input {height: 100%;border: none;padding-left:0px;}'
  ].join("");
  style.innerHTML = styleText;
  document.getElementsByTagName('body')[0].appendChild(style);

  let dftConfig = {
        moutEl:''
        ,data:[]
        ,dataFormat:{
          //默认类型是array，或json
          type:'array'
          //如果是json请指定一个数据字段属性
          ,dataField:''
        }
        ,prop:{
          name:'name'
          ,value:'id'
          //多名称模式
          ,multipleName:{
            enable:false
            //你要显示的字段
            ,names:[]
            ,separate:'-'
          }
        }
        //tag标签项id属性值，默认区data每一项的id属性值，否则取在data中的索引
        ,valModel:'id'
        //是否开启新增标签
        ,enableNewTag:false
        ,newAddTag:{
          name:'+ 新增标签'
          ,id:'new-add-tag'
        }
        //是否可重复添加
        ,isRepeatable:false
        //是否启用关闭标签
        ,enableClose:false
        //是否启用可重新创建
        ,enableReNew:false
        //是否开启工具栏
        ,enableInfoBar:true
        ,toolBar:['CLEAR']//'EDIT'
        ,async:{
          enable:false
          ,url:''
          ,type:'post'
          ,data:null
          ,dataType: 'json'
          ,contentType:'application/x-www-form-urlencoded'
        }
        ,events:{
          beforeRender(data){return data;}
          ,addTagEvt(e,addEl,id){}
          ,deledTagEvt(data){}
          ,clearedTagEvt(data){}
        }
      }
  //实例配置存储器
  , configs = []
  //实例对象存储器
  , insStorage = []

  , curTagIns = null

  , Tag = function(options){
    this.options = options;
    this.moutEl = Tag.utils.isDOMEl(options.moutEl)?options.moutEl:document.querySelector(options.moutEl);
    this.ready();
  };

  Tag.utils = {
    //查询对象是否存在
    isHasConfig(moutElAttr){
      for(let i = 0,insLen = configs.length; i < insLen; i++){
        let config = configs[i]
        if(config.moutEl == moutElAttr){
          return {config:config,index:i};
        }
        return false;
      }
    }
    ,inArray(arr,val){
      for(let len = arr.length, i = 0; i < len ; i++){
        if(typeof val == 'function'){
          let isInclude = val.call(arr,arr[i],i)
          if(isInclude){
            return {data:arr[i],index:i};
          }
        }else{
          if(arr[i] == val){
            return {data:arr[i],index:i};
          }
        }
      }
      return -1;
    }
    , isDOMEl:typeof HTMLElement === 'object'
        ? function(obj) {return obj instanceof HTMLElement;}
        : function(obj) {return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';}
    , rightData(tagIns,data){
      let options = tagIns.options
          , key = options.dataFormat.dataField
      return Array.isArray(data) && data.length>0 || typeof data == 'object' && options.dataFormat.type == 'json' && data[key] && Array.isArray(data[key]) && data[key].length>0
    }
    , getTagItem(layId,options){
      for(let i=0, tagLen = options.data.length; i<tagLen; i++){
        let tag = options.data[i]
        if(options.valModel == 'id'){
          if(tag[options.prop.value] == layId){
            return {tagItem:tag,index:i}
          }
        }else{
          if(tag == layId){
            return tag;
          }
        }
      }
    }
    , headWordToUpCase(str){
      return str.toLowerCase().replace(/\b(\w)|\s(\w)/g,function(word){return word.toUpperCase()})
    }
    , isType(type){
      return (obj)=>{
        type = Tag.utils.headWordToUpCase(type);
        return Object.prototype.toString.call(obj) == `[object ${type}]`;
      }
    }
    ,getCurIns(){
       return curTagIns;
    }
    ,setCurIns(ins){
       curTagIns = ins
    }
    ,getIns(moutEl){
      return insStorage.filter(ins=>{
        return ins.moutEl == moutEl
      })[0]
    }
  };

  Tag.prototype = {
    constructor:Tag
    ,ready(){
      this.getDataWay()
    }
    ,getTagValue(tagItem){
      let options = this.options
      if(options.valModel == 'id'){
        return tagItem[options.prop.value]
      }else{
        return tagItem;
      }
    }
    ,getTagName(tagItem){
      let options = this.options
          , multipleName = options.prop.multipleName
          , separate = multipleName.separate
      if(!multipleName.enable){
        return tagItem[options.prop.name]
      }else{
        let showName = ''
        multipleName.names.forEach(name=>{
          if(tagItem[name]){
            showName += tagItem[name]+separate
          }
        })
        return showName.substring(0,showName.lastIndexOf(separate))
      }
    }
    ,uniArr(data){
      let options = this.options
          ,tagData = this.options.data
          ,key = ''
          ,itemObj = null
      if(options.valModel == 'id'){
        key = options.prop.value
      }

      for(let i = 0; i < data.length; i++){
        let addTag = data[i];
        if(key){
          itemObj = Tag.utils.inArray(tagData,function(tag){
            return tag[key] == addTag[key]
          });
        }else{
          itemObj = Tag.utils.inArray(tagData,addTag);
        }
        if(itemObj != -1){
          data.splice(i,1)
          i--;
        }
      }
      return data;
    }
    ,getDataWay(){
      let options = this.options
          ,async = options.async
      if(async.enable){
        let deffer = $.ajax({
          url:async.url
          ,data:async.data
          ,dataType:'json'
          ,contentType:options.contentType
        });
        deffer.done(resObj=>{
          if(!Array.isArray(resObj) || (Array.isArray(resObj) && !resObj.length)){
            $(this.options.moutEl).empty();
            return;
          }
          this.renderCycle(resObj,true);
        })
        deffer.fail(resObj=>{
          console.log('ajax请求失败！',resObj)
        })
      }else{
        let data = this.options.data
        if(!Array.isArray(data) || (Array.isArray(data) && !data.length)){
          $(this.options.moutEl).empty();
          return;
        }
        this.renderCycle(data,true);
      }
    }
    //渲染周期
    ,renderCycle(data,isBindEvt){
      data = this.beforeRender(data);
      let options = this.options
      let key = options.dataFormat.dataField
      if(data && Tag.utils.rightData(this,data)){
        let isObject = Tag.utils.isType('object')
        if(isObject(data)){
          data = data[key]
        }
        options.data = data;
        this.render(data);
        isBindEvt && this.rendered();
      }
    }
    ,beforeRender(data){
      data = this.options.events.beforeRender.call(this,data);
      if(data && Tag.utils.rightData(this,data)){
        this.options.data = data;
        return data;
      }
      return false;
    }
    ,render(data){
      let renderTag = function(options){
        let value = options.prop.value
            , html = '<div class="tags-container">'
        data.forEach(item => {
          html += '<button id="'+this.getTagValue(item)+'" lay-id="'+this.getTagValue(item)+'" type="button" class="layui-btn-primary layui-btn-sm tag-item">'+this.getTagName(item)
          if(options.enableClose){
            html += '<i class="layui-icon layui-unselect tag-close">&#x1006;</i>'
          }
          html += '</button>'
        });
        if(options.enableNewTag){
          html += ' <button id="'+options.newAddTag.id+'" type="button" class="layui-btn-primary layui-btn-sm tag-item new-add-tag">'+options.newAddTag.name+'</button>';
        }
        if(options.enableInfoBar){
          let titles = {
            'CLEAR':'清空'
            ,'EDIT':'修改'
            ,'ADD':'新增'
          }
          html+= '<div class="tag-tool layui-btn-container">'
          options.toolBar.forEach(bar=>{
            if(titles[bar]){
              html+= '<button type="button" class="layui-btn layui-btn-xs layui-btn-warm float-right toolbar-btn" id="tag-'+bar.toLowerCase()+'">'+titles[bar]+'</button>'
            }
          })
          html+= '<div class="info-bar">已选中<span class="tag-count">'+data.length+'</span>条</div>'
          html+= '</div>'
        }
        html += '</div>';
        $(options.moutEl).append(html).show();
      };
      renderTag.call(this,this.options);
    }
    ,rendered(){
      let _self = this
      , options = _self.options
      if(options.enableClose){
        $(options.moutEl).on('click','.tag-close',function(e){
          _self.delTag(this)
        })
      }
      if(options.enableInfoBar){
        $(options.moutEl).on('click','.toolbar-btn',function(e){
          e = window.event || e
          let id = $(this).attr('id')
              , events = {
            'tag-clear':function(e,clearEl,id){
              _self.clearTag();
            }
            ,'tag-edit':function(e,editEl,id){

            }
            ,'tag-add':function(e,addEl,id){
              let addTagEvt = options.events.addTagEvt
              typeof addTagEvt == 'function' && addTagEvt.call(_self,e,addEl,id)
            }
          }
          events[id].call(_self,e,this,id)
        })
      }
    }
    //data必须是数组
    ,addTag(data){
      if(!Array.isArray(data) && data.length>0){return 'data 参数必须是一个非空数组';}
      let options = this.options
          , $newAddTag = $(options.moutEl).children('.new-add-tag')
          , html = ''
      data = options.isRepeatable ? data : this.uniArr(data);
      this.options.data = options.data.concat(data);
      if(data.length > 0){
        data.forEach((item)=>{
          html += '<button id="'+this.getTagValue(item)+'" lay-id="'+this.getTagValue(item)+'" type="button" class="layui-btn-primary layui-btn-sm tag-item">'+this.getTagName(item)
          if(options.enableClose){
            html += '<i class="layui-icon layui-unselect tag-close">&#x1006;</i>'
          }
          html += '</button>'
        });
        $newAddTag[0] ? $newAddTag.before(html) : (options.enableInfoBar ? $(options.moutEl).find('.tag-tool').before(html) : $(options.moutEl).children().append(html));
        options.enableInfoBar && this.setTagCount(this.options.data.length)
      }
    }
    ,delTag(tagEl){
      let options = this.options
          , $tag = $(tagEl).parent()
          , layId = $tag.attr('lay-id')
          , tagItem = Tag.utils.getTagItem(layId,options);
      tagItem && (
          $tag.remove()
          ,options.data.splice(tagItem.index,1)
          ,this.setTagCount(options.data.length)
          , typeof options.events.deledTagEvt == 'function' && options.events.deledTagEvt.call(this,options.data)
      );
    }
    ,setTagCount(count){
      $(this.options.moutEl).find('.tag-count').text(count||0)
    }
    ,clearTag(){
      let options = this.options
      $(options.moutEl).find('.tag-item').not('.tag-tool,.new-add-tag').remove();
      this.setTagCount(0)
      options.data = [];
      typeof options.events.clearedTagEvt == 'function' && options.events.clearedTagEvt.call(this,options.data);
    }
    ,reRender(options){
      this.options.data = []
      $.extend(true,this.options,options);
      $(this.options.moutEl).hide().empty();
      this.render(this.options.data);
    }
  };

  let render = function(options){
    options = $.extend(true,{},dftConfig,options);
    let config = Tag.utils.isHasConfig(options.moutEl);

    //不存在或者 存在但可被重新创建
    if(!config || (config.config && options.enableReNew)){
      if(!config){
        let tag = new Tag(options);
        configs.push(options) && insStorage.push(tag)
        return tag;
      }else{
        if(!Array.isArray(config.config.data)|| Array.isArray(config.config.data) && config.config.data.length==0){
          return insStorage[config.index]
        }
        let tag = new Tag(options);
        configs.splice(config.index,1,options)
        return tag;
      }
    }
    //已存在，这返回这个对象
    return insStorage[config.index];
  };

  return {render,configs,insStorage,getIns:Tag.utils.getIns,destroy:Tag.utils.destroy};

});